package weeny.face;

import static java.util.Collections.EMPTY_LIST;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import weeny.util.io.IOUtil;
import weeny.util.logs.Debug;

/**
 * create the load & save control for anything. This is a box including load and
 * save buttons. To use it, you need to define the behaviors by implementing 
 * {@link #loadData(InputStream)} and {@link #saveData(OutputStream)}
 * 
 * Also, you can add listeners before/after load/save. To get the name of the
 * file you are about to save to or load from, **USE ActionEvent.getSource()**.
 * 
 * Also, there is a {@link LoadSaveBox#withInputField(LoadSaveBox)} function.
 * Call it, you will get a input text field such that you can change the default
 * file name on the fly.
 */

public abstract class LoadSaveBox extends Box{
	
	/**
	 * This function is called when you press the "load" button. Before it is
	 * called, every action in {@link LoadSaveBox#preLoadListener} is performed.
	 * On the other hand, every action in {@link LoadSaveBox#postLoadListener}
	 * is also performed after this function is called.
	 * 
	 * @param fileName the absolute path of the target file
	 */
	abstract public void loadData(InputStream input);
	/**
	 * This function is called when you press the "save" button. Before it is
	 * called, every action in {@link LoadSaveBox#preSaveListener} is performed.
	 * On the other hand, every action in {@link LoadSaveBox#postSaveListener}
	 * is also performed after this function is called.
	 * 
	 * @param fileName the absolute path of the target file
	 */
	abstract public void saveData(OutputStream output);

	/**
	 * Keep the default file path to reduce the steps of interaction.
	 * Set the default file name for further use. For more info, please
	 * see {@link IOUtil#targetFile(ActionEvent, String, boolean)}.
	 */
	private String defaultFile = null;
	public void setDefaultFile(String fileName){
		this.defaultFile = fileName;
	}
	
	//pre/post listeners are called before and each action
	//in case what to do some preparation or patching stuff
	private List<ActionListener>  preLoadListener = EMPTY_LIST;
	private List<ActionListener> postLoadListener = EMPTY_LIST;
	private List<ActionListener>  preSaveListener = EMPTY_LIST;
	private List<ActionListener> postSaveListener = EMPTY_LIST;
	/**
	 * Added listeners will be called before/after the actual load/save action.
	 * To get the name of the file you are about to save to or load from, please
	 * use {@link ActionEvent#getSource())}.
	 */
	public void addPreLoadAction(ActionListener listener){
		if (preLoadListener == EMPTY_LIST) {
			preLoadListener = new ArrayList<ActionListener>();
		}
		preLoadListener.add(listener);
	}
	/**
	 * @see #addPreLoadAction(ActionListener)
	 */
	public void addPostLoadAction(ActionListener listener){
		if (postLoadListener == EMPTY_LIST) {
			postLoadListener = new ArrayList<ActionListener>();
		}
		postLoadListener.add(listener);
	}
	/**
	 * @see #addPreLoadAction(ActionListener)
	 */
	public void addPreSaveAction(ActionListener listener){
		if (preSaveListener == EMPTY_LIST){
			preSaveListener = new ArrayList<ActionListener>();
		}
		preSaveListener.add(listener);
	}
	/**
	 * @see #addPreLoadAction(ActionListener)
	 */
	public void addPostSaveAction(ActionListener listener){
		if (postSaveListener == EMPTY_LIST){
			postSaveListener = new ArrayList<ActionListener>();
		}
		postSaveListener.add(listener);
	}
	
	/**
	 * Wrap the passed-in LoadSaveBox with a fieldInputBox, so you can change
	 * the {@link #defaultFile} on the user interface.
	 * @param loadSave
	 * @return
	 */
	public static Box withInputField(final LoadSaveBox loadSave){
		Box box = Box.createVerticalBox();
		box.setBorder(loadSave.getBorder());
		loadSave.setBorder(null);
		Box field = Box.createHorizontalBox();
		field.add(new JLabel("default: "));
		final JTextField input = new JTextField();
		input.setText(loadSave.defaultFile==null? "" : loadSave.defaultFile);
		input.addKeyListener(new KeyListener() {
			public void keyTyped(KeyEvent e) {
				if (input.getText().length()==0) {					
					loadSave.setDefaultFile(null);					
				}
				else{
					loadSave.setDefaultFile(input.getText());
				}
				Debug.info("Default file is: "+loadSave.defaultFile);
			}
			public void keyReleased(KeyEvent e) {}
			public void keyPressed(KeyEvent e) {}
		});
		field.add(input);
		box.add(field);
		box.add(loadSave);
		return box;
	}
	
	/**
	 * create a contol widget with load/save buttons.
	 * 
	 * @param dftFile the default file for load and save, which can be null.
	 * Actually, the actual target file is calculated a little more complicated.
	 * Please see {@link IOUtil#targetFile(ActionEvent, String, boolean)} for
	 * more details.
	 * 
	 * @param title the label will be shown on the widget for distinction.
	 */
	public LoadSaveBox(String dftFile, String title){
		super(BoxLayout.X_AXIS);
		defaultFile = dftFile;
		setBorder(new TitledBorder("Load & Save ("+title+")"));
		JButton load = new JButton("Load Data");
		load.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String file = IOUtil.targetFile(e, defaultFile, true);
				if (file == null) {
					Debug.prob("Fail to find the targetFile for save or load");
					return;
				}
				else{
					Object oldSource = e.getSource();
					e.setSource(file);
					Debug.info("Load file from: "+file);
					for(ActionListener lns : preLoadListener){
						lns.actionPerformed(e);
					}
					try {
						loadData(new FileInputStream(file));
					} catch (FileNotFoundException e1) {
						e1.printStackTrace();
					}
					for(ActionListener lns : postLoadListener){
						lns.actionPerformed(e);
					}
					e.setSource(oldSource);
				}
			}
		});
		JButton save = new JButton("Save Data");
		save.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String file = IOUtil.targetFile(e, defaultFile, false);
				if (file == null) {
					return;
				}
				else{
					Object oldSource = e.getSource();
					e.setSource(file);
					Debug.info("Save file: "+new File(file).getAbsolutePath());
					for(ActionListener lis : preSaveListener){
						lis.actionPerformed(e);
					}
					try {
						saveData(new FileOutputStream(file));
					} catch (FileNotFoundException e1) {
						e1.printStackTrace();
					}
					for(ActionListener lis : postSaveListener){
						lis.actionPerformed(e);
					}
					e.setSource(oldSource);
				}
			}
		});
		add(Box.createHorizontalGlue());
		add(load);
		add(save);
		add(Box.createHorizontalGlue());
	}
}